package com.yks.hbo.amazon.util;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class HttpUtility {

    private static Logger LOG = LogManager.getLogger(HttpUtility.class);

    private static Charset CHARSET = Charset.forName("UTF-8");

    private static int CONNECTION_REQUEST_TIMEOUT = 120000;
    private static int SOCKET_TIMEOUT = 120000;
    private static int CONNECT_TIMEOUT = 120000;
    public static CloseableHttpClient httpclient = null;
    static {
        // 初始化线程池
        RequestConfig params = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT)
                .setExpectContinueEnabled(true).build();

        PoolingHttpClientConnectionManager pccm = new PoolingHttpClientConnectionManager();
        pccm.setMaxTotal(60000); // 连接池最大并发连接数
        pccm.setDefaultMaxPerRoute(60000); // 单路由最大并发数
        httpclient = HttpClients.custom().setConnectionManager(pccm).setDefaultRequestConfig(params)
                .build();
    }

    public static String httpGet(String url) {
        CloseableHttpClient httpCilent = HttpClients.createDefault();// Creates CloseableHttpClient instance with default configuration.
        HttpGet httpGet = new HttpGet(url);
        try {
            HttpResponse httpResponse = httpCilent.execute(httpGet);
            return EntityUtils.toString(httpResponse.getEntity(), "utf-8");
        } catch (IOException e) {
            LOG.error(String.format("httpGet:%s", httpGet), e);
        } finally {
            try {
                httpCilent.close();// 释放资源
            } catch (IOException e) {
                LOG.error(e.toString(), e);
            }
        }
        return "";
    }

    public static String httpPost(String url, String xmlData) {
        // Creates CloseableHttpClient instance with default configuration.
        CloseableHttpClient httpCilent = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost(url);
        try {
            InputStreamEntity reqEntity = new InputStreamEntity(new ByteArrayInputStream(xmlData.getBytes()));
            reqEntity.setContentType("application/xml");
            reqEntity.setContentEncoding("utf-8");
            reqEntity.setChunked(true);
            httpPost.setEntity(reqEntity);
            HttpResponse httpResponse = httpCilent.execute(httpPost);
            return EntityUtils.toString(httpResponse.getEntity(), "utf-8");

        } catch (IOException e) {
            LOG.error(String.format("httpPost:%s", httpPost), e);
        } finally {
            try {
                httpCilent.close();// 释放资源
            } catch (IOException e) {
                LOG.error(e.toString(), e);
            }
        }
        return "";
    }

    /**
     * 向指定 URL 发送GET方法的请求
     *
     * @param url   发送请求的 URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     * @throws Exception
     */
    public static String sendGet(String url, String param) throws Exception {
        String result = "";
        BufferedReader in = null;
        try {
            String urlName = url;
            if (param != null && !"".equals(param)) {
                urlName += "?" + param;
            }
            URL realUrl = new URL(urlName);
            URLConnection conn = realUrl.openConnection();// 打开和URL之间的连接
            conn.setRequestProperty("accept", "*/*");// 设置通用的请求属性
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
            conn.setConnectTimeout(4000);
            conn.connect();// 建立实际的连接
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));// 定义BufferedReader输入流来读取URL的响应
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            LOG.error("发送get请求出错,message={}", e.getMessage());
            throw new Exception(e.getMessage());
        } finally {// 使用finally块来关闭输入流
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                LOG.error("关闭流出错,message={}", ex.getMessage());
                throw new Exception(ex.getMessage());
            }
        }
        return result;
    }



    /**
     * 上传文件
     *
     * @param url      服务器地址
     * @param filePath 本地文件路径，全路径。
     * @return String
     */
    public static String postFile(String url, String filePath) {
        HttpClient httpClient = HttpClients.createDefault();
        String result = null;

        HttpPost httpPost = new HttpPost(url);

        httpPost = setHttpPostConfig(httpPost);

        File file = new File(filePath);
        FileBody bin = new FileBody(file);

        HttpEntity reqEntity = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
                .addPart("file", bin).setCharset(CHARSET).build();
        httpPost.setEntity(reqEntity);
        HttpResponse httpResponse = null;
        try {
            //执行请求
            httpResponse = httpClient.execute(httpPost);
            int webStatus = httpResponse.getStatusLine().getStatusCode();

            if (200 == webStatus) {
                HttpEntity responseEntity = httpResponse.getEntity();
                if (null != responseEntity) {
                    result = EntityUtils.toString(responseEntity, CHARSET);
                }
                //LOG.info("success to post file: [url={}, filePath={}, result={}]", url, filePath, result);日志量大, 暂时注释

                EntityUtils.consume(responseEntity);
            } else {
                LOG.error("fail to post file:  [url={}, filePath={}, status={}]", url, filePath, webStatus);
            }
        } catch (IOException e) {
            LOG.error("exception to post file: [url={}, filePath={}]", url, filePath, e);
        }
        return result;
    }

    /**
     * 获取文件
     *
     * @param url 文件地址
     * @return InputStream
     */
    public static InputStream getFile(String url) {
        InputStream inputStream = null;

        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse httpResponse = null;
        HttpGet httpGet = null;
        String newUrl = "";
        try {

            //路径最后一级进行url加密
            int lastPathPosition = url.lastIndexOf("/") + 1;
            String lastPath = url.substring(lastPathPosition, url.length());
            newUrl = url.substring(0, lastPathPosition) + URLEncoder.encode(lastPath, CHARSET.displayName());

            //LOG.info("URLEncoder encode newUrl: {}", newUrl);日志量大, 暂时注释
            httpGet = new HttpGet(newUrl);

            httpGet = setHttpGetConfig(httpGet);

            // 执行请求
            httpResponse = httpClient.execute(httpGet);

            int webStatus = httpResponse.getStatusLine().getStatusCode();

            if (200 == webStatus) {
                inputStream = httpResponse.getEntity().getContent();
            } else {
                LOG.error("fail to get file:  [url={}, status={}]", newUrl, webStatus);
            }

        } catch (IOException e) {
            LOG.error("exception to get file:  [url={}]", newUrl, e);
        }

        return inputStream;
    }

    /**
     * 设置超时参数
     *
     * @param httpPost httpPost
     * @return HttpPost
     */
    private static HttpPost setHttpPostConfig(HttpPost httpPost) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).build();
        httpPost.setConfig(requestConfig);
        return httpPost;
    }

    /**
     * 设置超时参数
     *
     * @param httpGet httpGet
     * @return HttpGet
     */
    private static HttpGet setHttpGetConfig(HttpGet httpGet) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).build();
        httpGet.setConfig(requestConfig);
        return httpGet;
    }

    public static String postForm(String urlToRequest, Map<String, String> paramMap, Map<String, String> headerMap){
        try {
            return postFormWithException(urlToRequest, paramMap, headerMap);
        } catch (Exception ex) {
            LOG.error(String.format("%s\r\n%s\r\n%s", urlToRequest, JSONObject.toJSONString(paramMap), JSONObject.toJSONString(headerMap)), ex);
        }
        return "";
    }

    public static String postFormWithException(String urlToRequest, Map<String, String> paramMap, Map<String, String> headerMap) throws Exception {
        Long startTs = System.currentTimeMillis();

        try {
            HttpPost post = new HttpPost(urlToRequest);

            {
                RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(20000)
                        .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).setSocketTimeout(CONNECTION_REQUEST_TIMEOUT).build();
                post.setConfig(requestConfig);
            }
            // 设置参数
            if (headerMap != null && !headerMap.isEmpty()) {
                for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue());
                }
            }

            //装填参数
            List<NameValuePair> nvps = new ArrayList<NameValuePair>(16);
            if (paramMap != null && !paramMap.isEmpty()) {
                for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                    nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
            }

            //StringEntity se = new StringEntity(paramBody);
            post.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));

            String result = invoke(post);
            Long endTs = System.currentTimeMillis();
            Long currentMethodCallTime = endTs - startTs;
            //LOG.info("url:{},call time {} ms", urlToRequest, currentMethodCallTime);日志量大, 暂时注释
            if (currentMethodCallTime > 5000) {
                LOG.info("所有存活线程=" + Thread.getAllStackTraces().size());
            }
//            LOGGER.info("post-rps:{}", result);
            return result;
        } catch (Exception e) {
            LOG.error("[HttpClientUtils][post][Unsupported Encoding Exception]", e);
            throw e;
        }
    }

    private static String invoke(HttpUriRequest request) throws Exception {
        CloseableHttpResponse response = null;
        try {
            response = httpclient.execute(request);
//            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
            {
                HttpEntity entity = response.getEntity();
                return EntityUtils.toString(entity, "UTF-8");
            }
        } catch (Exception e) {
            LOG.error(
                    "[HttpClientUtils][invoke][method:" + request.getMethod() + " URI:" + request.getURI() + "] is request exception", e);
            throw e;
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    LOG.error(
                            "[HttpClientUtils][invoke][method:" + request.getMethod() + " URI:" + request.getURI() + "] is closed exception",
                            e);
                }
            }
        }
//        return StringUtility.Empty;
    }

    public static String postStringWithException(String urlToRequest, String paramBody, Map<String, String> headerMap) throws Exception {
        Long startTs = System.currentTimeMillis();

//            LOGGER.info("post-req:url:{},param:{}", urlToRequest, JSON.toJSONString(parameters));
        HttpPost post = new HttpPost(urlToRequest);

        {
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT)
                    .setConnectionRequestTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
            post.setConfig(requestConfig);
        }
        // 设置参数
        if (headerMap != null && !headerMap.isEmpty()) {
            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                post.setHeader(entry.getKey(), entry.getValue());
            }
        }
        StringEntity se = new StringEntity(paramBody, "utf-8");
        post.setEntity(se);
        String result = invoke(post);
        Long endTs = System.currentTimeMillis();
        Long currentMethodCallTime = endTs - startTs;
        //LOG.info("url:{},call time {} ms", urlToRequest, currentMethodCallTime);日志量大, 暂时注释
        if (currentMethodCallTime > 5000) {
            LOG.info("所有存活线程=" + Thread.getAllStackTraces().size());
        }
//            LOGGER.info("post-rps:{}", result);
        return result;
    }


    public static String deleteWithException(String urlToRequest, Map<String, String> headerMap) throws Exception {
        Long startTs = System.currentTimeMillis();
        HttpDelete httpDelete = new HttpDelete(urlToRequest);

        {
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT)
                    .setConnectionRequestTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
            httpDelete.setConfig(requestConfig);
        }
        // 设置参数
        if (headerMap != null && !headerMap.isEmpty()) {
            for (Map.Entry<String, String> entry : headerMap.entrySet()) {
                httpDelete.setHeader(entry.getKey(), entry.getValue());
            }
        }
        String result = invoke(httpDelete);
        Long endTs = System.currentTimeMillis();
        Long currentMethodCallTime = endTs - startTs;
        //LOG.info("url:{},call time {} ms", urlToRequest, currentMethodCallTime);日志量大, 暂时注释
        if (currentMethodCallTime > 5000) {
            LOG.info("所有存活线程=" + Thread.getAllStackTraces().size());
        }
        return result;
    }

    public static String postFormWithRetry(String url,Map<String, String> paramMap,Map<String, String> headerMap,int retry){

        String result = null;

        try {
            result =  HttpUtility.postFormWithException(url, paramMap, headerMap);
        } catch (Exception e) {
            LOG.error(String.format("postFormWithRetry  %s\r\n%s\r\n%s", url, JSONObject.toJSONString(paramMap), JSONObject.toJSONString(headerMap)), e);
        }
        if(retry>0){
            if(StringUtils.isEmpty(result)){
                LOG.info("postFormRetry {} retry {}",url,retry);
                try {
                    Thread.sleep(5000);//等待5秒后重试
                    retry--;
                    postFormWithRetry(url,paramMap,headerMap,retry);
                } catch (InterruptedException e) {
                    LOG.error("postStringFormWithRetry failed",e);
                }
            }
        }
        return result;
    }

    public static String postStringFormWithRetry(String url,String paramBody,Map<String, String> headerMap,int retry){

        String result = null;

        try {
            result =  HttpUtility.postStringWithException(url, paramBody, headerMap);
        } catch (Exception e) {
            LOG.error(String.format("%s\r\n%s\r\n%s", url, JSONObject.toJSONString(paramBody), JSONObject.toJSONString(headerMap)), e);
        }
        if(retry>0){
            if(StringUtils.isEmpty(result)){
                LOG.info("postStringFormWithRetry {} retry {}",url,retry);
                try {
                    Thread.sleep(10000);//等待5秒后重试
                    retry--;
                    return postStringFormWithRetry(url,JSONObject.toJSONString(paramBody),headerMap,retry);
                } catch (InterruptedException e) {
                    LOG.error("postStringFormWithRetry failed",e);
                }
            }
        }
        return result;
    }
    /**
     * 检测url是否可以范问到
     * @param urlStr
     * @return
     * @author: songxulin
     * @date :  2020-09-01 15:57
     */
    public static boolean checkUrlExist(String urlStr){
        boolean isExist;
        try{
            // 注：urlStr中需将空格替换为%20,否则报505
            URL url = new URL(urlStr);
            HttpURLConnection conn = (HttpURLConnection ) url.openConnection();
            int state = conn.getResponseCode();
            if(state == 200){
                isExist  = true;
            }else{
                isExist  = false;
            }
        }catch(Exception e) {
            isExist = false;
        }
        return isExist;
    }
}
